﻿#include <mutex>
#include <condition_variable>
#include <queue>
#include <list>
#include <atomic>
#include "workcondition.h"
#include "workservice.h"

struct EventRecord{
    WorkService::EventType event;
    WorkService::EventId id;
};

struct WorkService::Private
{
public:
    Private(){
        m_statue = Active;
        maxId = 1;
    }
    enum Statue{
        None,
        Active,
        Closed
    };

    std::list<EventRecord> m_eventQueue;
    Statue m_statue;
    std::condition_variable m_workerCondition;
    std::mutex m_mutex;
    WorkCondition m_wc;

    EventId maxId;
};

WorkService::WorkService():_P(new Private)
{

}

WorkService::~WorkService()
{
    close();
}

void WorkService::Cancel(WorkService::EventId event)
{
    std::lock_guard<std::mutex> lock(_P->m_mutex);
    for(auto it = _P->m_eventQueue.begin();it != _P->m_eventQueue.end();it++){
        if(it->id == event){
            _P->m_eventQueue.erase(it);
            return;
        }
    }
}

WorkService::EventId WorkService::Post(const WorkService::EventType &o)
{
    std::lock_guard<std::mutex> lock(_P->m_mutex);
    if(_P->m_statue != Private::Active){
        return 0;
    }
    EventRecord e = {o,_P->maxId};
    _P->maxId++;
    _P->m_eventQueue.push_back(e);
    _P->m_workerCondition.notify_one();
}

void WorkService::ProcessOne()
{
    auto f = PullOne();
    if(f){
        f();
    }
}

void WorkService::ProcessAll()
{
    auto f = PullAll();
    for(auto &i:f){
        i();
    }
}

void WorkService::Run()
{
    std::unique_lock<std::mutex> lock(_P->m_mutex);
    if(_P->m_statue == Private::Closed){
        return;
    }
    WorkConditionGurad wcg(_P->m_wc);
    while (true) {

        if(_P->m_statue == Private::Closed){
            return;
        }

        while (_P->m_eventQueue.empty()) {
            _P->m_workerCondition.wait(lock);
            if(_P->m_statue == Private::Closed){
                return;
            }
        }

        EventRecord e = _P->m_eventQueue.front();
        _P->m_eventQueue.pop_front();

        _P->m_mutex.unlock();
        e.event();
        _P->m_mutex.lock();
    }
}

void WorkService::close()
{
    std::unique_lock<std::mutex> lock(_P->m_mutex);

    _P->m_statue = Private::Closed;
    _P->m_eventQueue.clear();
    _P->m_workerCondition.notify_all();
    _P->m_wc.WaitForFinished();
    _P->m_statue = Private::Active;
}

WorkService::EventType WorkService::PullOne()
{
    std::lock_guard<std::mutex> lock(_P->m_mutex);
    if(_P->m_statue != Private::Active){
        return EventType();
    }
    if(_P->m_eventQueue.empty()){
        return EventType();
    }

    auto ret = _P->m_eventQueue.front();
    _P->m_eventQueue.pop_front();
    return ret.event;
}

std::vector<WorkService::EventType> WorkService::PullAll()
{
    std::lock_guard<std::mutex> lock(_P->m_mutex);
    if(_P->m_statue != Private::Active){
        return std::vector<WorkService::EventType>();
    }
    std::vector<WorkService::EventType> ret;
    while (!_P->m_eventQueue.empty()) {
        ret.push_back(_P->m_eventQueue.front().event);
        _P->m_eventQueue.pop_front();
    }
    return ret;
}
